Celem pracy domowej jest przygotowanie wstępnej analizy eksploracyjnej danych. Zbiór danych, który był analizowany dotyczy win i znajduje się pod tym linkiem https://www.kaggle.com/uciml/red-wine-quality-cortez-et-al-2009. Cała analiza została przeprowadzona w pythonie z wykorzystaniem bibliotek: pandas, numpy, plotly oraz matplotlib
import pandas as pd
import numpy as np
import plotly.express as px
import plotly.graph_objs as go
import matplotlib.pyplot as plt
df = pd.read_csv("winequality-red.csv")
df.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 1599 entries, 0 to 1598 Data columns (total 12 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 fixed acidity 1599 non-null float64 1 volatile acidity 1599 non-null float64 2 citric acid 1599 non-null float64 3 residual sugar 1599 non-null float64 4 chlorides 1599 non-null float64 5 free sulfur dioxide 1599 non-null float64 6 total sulfur dioxide 1599 non-null float64 7 density 1599 non-null float64 8 pH 1599 non-null float64 9 sulphates 1599 non-null float64 10 alcohol 1599 non-null float64 11 quality 1599 non-null int64 dtypes: float64(11), int64(1) memory usage: 150.0 KB
The Red Wine Quality dataset zawiera informacje na temat różnych fizykochemicznych właściwości win.
Ten dataset zawiera tylko zmienne numeryczne:
null występuje w poszczególnych kolumnach?¶df.isnull().sum()
fixed acidity 0 volatile acidity 0 citric acid 0 residual sugar 0 chlorides 0 free sulfur dioxide 0 total sulfur dioxide 0 density 0 pH 0 sulphates 0 alcohol 0 quality 0 dtype: int64
Widzimy, że nasze dane nie posiadają żadnych braków i zawierają wszelkie potrzebne informacje.
Zobaczmy jak prezentują się ich rozkłady
#musimy zmienic typ wartosci quality
df.hist(bins=30, figsize=(15, 10))
plt.show
<function matplotlib.pyplot.show(close=None, block=None)>
Co od razu rzuca sie w oczy to fakt, że część zmiennych jest rozkładu przybliżonego do normalnego np. ph oraz część ma rozkłady typowo skośne np. total sulfur dioxide
df['fixed acidity'].describe()
count 1599.000000 mean 8.319637 std 1.741096 min 4.600000 25% 7.100000 50% 7.900000 75% 9.200000 max 15.900000 Name: fixed acidity, dtype: float64
df['volatile acidity'].describe()
count 1599.000000 mean 0.527821 std 0.179060 min 0.120000 25% 0.390000 50% 0.520000 75% 0.640000 max 1.580000 Name: volatile acidity, dtype: float64
df['citric acid'].describe()
count 1599.000000 mean 0.270976 std 0.194801 min 0.000000 25% 0.090000 50% 0.260000 75% 0.420000 max 1.000000 Name: citric acid, dtype: float64
df['residual sugar'].describe()
count 1599.000000 mean 2.538806 std 1.409928 min 0.900000 25% 1.900000 50% 2.200000 75% 2.600000 max 15.500000 Name: residual sugar, dtype: float64
df['chlorides'].describe()
count 1599.000000 mean 0.087467 std 0.047065 min 0.012000 25% 0.070000 50% 0.079000 75% 0.090000 max 0.611000 Name: chlorides, dtype: float64
df['free sulfur dioxide'].describe()
count 1599.000000 mean 15.874922 std 10.460157 min 1.000000 25% 7.000000 50% 14.000000 75% 21.000000 max 72.000000 Name: free sulfur dioxide, dtype: float64
df['total sulfur dioxide'].describe()
count 1599.000000 mean 46.467792 std 32.895324 min 6.000000 25% 22.000000 50% 38.000000 75% 62.000000 max 289.000000 Name: total sulfur dioxide, dtype: float64
df['density'].describe()
count 1599.000000 mean 0.996747 std 0.001887 min 0.990070 25% 0.995600 50% 0.996750 75% 0.997835 max 1.003690 Name: density, dtype: float64
df['pH'].describe()
count 1599.000000 mean 3.311113 std 0.154386 min 2.740000 25% 3.210000 50% 3.310000 75% 3.400000 max 4.010000 Name: pH, dtype: float64
df['sulphates'].describe()
count 1599.000000 mean 0.658149 std 0.169507 min 0.330000 25% 0.550000 50% 0.620000 75% 0.730000 max 2.000000 Name: sulphates, dtype: float64
df['alcohol'].describe()
count 1599.000000 mean 10.422983 std 1.065668 min 8.400000 25% 9.500000 50% 10.200000 75% 11.100000 max 14.900000 Name: alcohol, dtype: float64
df['quality'].describe()
count 1599.000000 mean 5.636023 std 0.807569 min 3.000000 25% 5.000000 50% 6.000000 75% 6.000000 max 8.000000 Name: quality, dtype: float64
df.describe()
| fixed acidity | volatile acidity | citric acid | residual sugar | chlorides | free sulfur dioxide | total sulfur dioxide | density | pH | sulphates | alcohol | quality | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| count | 1599.000000 | 1599.000000 | 1599.000000 | 1599.000000 | 1599.000000 | 1599.000000 | 1599.000000 | 1599.000000 | 1599.000000 | 1599.000000 | 1599.000000 | 1599.000000 |
| mean | 8.319637 | 0.527821 | 0.270976 | 2.538806 | 0.087467 | 15.874922 | 46.467792 | 0.996747 | 3.311113 | 0.658149 | 10.422983 | 5.636023 |
| std | 1.741096 | 0.179060 | 0.194801 | 1.409928 | 0.047065 | 10.460157 | 32.895324 | 0.001887 | 0.154386 | 0.169507 | 1.065668 | 0.807569 |
| min | 4.600000 | 0.120000 | 0.000000 | 0.900000 | 0.012000 | 1.000000 | 6.000000 | 0.990070 | 2.740000 | 0.330000 | 8.400000 | 3.000000 |
| 25% | 7.100000 | 0.390000 | 0.090000 | 1.900000 | 0.070000 | 7.000000 | 22.000000 | 0.995600 | 3.210000 | 0.550000 | 9.500000 | 5.000000 |
| 50% | 7.900000 | 0.520000 | 0.260000 | 2.200000 | 0.079000 | 14.000000 | 38.000000 | 0.996750 | 3.310000 | 0.620000 | 10.200000 | 6.000000 |
| 75% | 9.200000 | 0.640000 | 0.420000 | 2.600000 | 0.090000 | 21.000000 | 62.000000 | 0.997835 | 3.400000 | 0.730000 | 11.100000 | 6.000000 |
| max | 15.900000 | 1.580000 | 1.000000 | 15.500000 | 0.611000 | 72.000000 | 289.000000 | 1.003690 | 4.010000 | 2.000000 | 14.900000 | 8.000000 |
df.duplicated().sum()
240
Nasza ramka danych posiada aż 240 tych samych wierszy
Zobaczmy może, które wiersze się powtarzają
df[df.duplicated()]
| fixed acidity | volatile acidity | citric acid | residual sugar | chlorides | free sulfur dioxide | total sulfur dioxide | density | pH | sulphates | alcohol | quality | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 4 | 7.4 | 0.700 | 0.00 | 1.90 | 0.076 | 11.0 | 34.0 | 0.99780 | 3.51 | 0.56 | 9.4 | 5 |
| 11 | 7.5 | 0.500 | 0.36 | 6.10 | 0.071 | 17.0 | 102.0 | 0.99780 | 3.35 | 0.80 | 10.5 | 5 |
| 27 | 7.9 | 0.430 | 0.21 | 1.60 | 0.106 | 10.0 | 37.0 | 0.99660 | 3.17 | 0.91 | 9.5 | 5 |
| 40 | 7.3 | 0.450 | 0.36 | 5.90 | 0.074 | 12.0 | 87.0 | 0.99780 | 3.33 | 0.83 | 10.5 | 5 |
| 65 | 7.2 | 0.725 | 0.05 | 4.65 | 0.086 | 4.0 | 11.0 | 0.99620 | 3.41 | 0.39 | 10.9 | 5 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 1563 | 7.2 | 0.695 | 0.13 | 2.00 | 0.076 | 12.0 | 20.0 | 0.99546 | 3.29 | 0.54 | 10.1 | 5 |
| 1564 | 7.2 | 0.695 | 0.13 | 2.00 | 0.076 | 12.0 | 20.0 | 0.99546 | 3.29 | 0.54 | 10.1 | 5 |
| 1567 | 7.2 | 0.695 | 0.13 | 2.00 | 0.076 | 12.0 | 20.0 | 0.99546 | 3.29 | 0.54 | 10.1 | 5 |
| 1581 | 6.2 | 0.560 | 0.09 | 1.70 | 0.053 | 24.0 | 32.0 | 0.99402 | 3.54 | 0.60 | 11.3 | 5 |
| 1596 | 6.3 | 0.510 | 0.13 | 2.30 | 0.076 | 29.0 | 40.0 | 0.99574 | 3.42 | 0.75 | 11.0 | 6 |
240 rows × 12 columns
(df['fixed acidity'] == 0).sum() / df.shape[0]
0.0
(df['volatile acidity'] == 0).sum() / df.shape[0]
0.0
(df['citric acid'] == 0).sum() / df.shape[0]
0.0825515947467167
(df['residual sugar'] == 0).sum() / df.shape[0]
0.0
(df['chlorides'] == 0).sum() / df.shape[0]
0.0
(df['free sulfur dioxide'] == 0).sum() / df.shape[0]
0.0
(df['total sulfur dioxide'] == 0).sum() / df.shape[0]
0.0
(df['density'] == 0).sum() / df.shape[0]
0.0
(df['pH'] == 0).sum() / df.shape[0]
0.0
(df['sulphates'] == 0).sum() / df.shape[0]
0.0
(df['alcohol'] == 0).sum() / df.shape[0]
0.0
(df['quality'] == 0).sum() / df.shape[0]
0.0
Widzimy, więc że wartości = 0 znajdują się tylko w kolumnie citric acid i stanowią one, aż 8% wszystkich wierszy w tej kolumnie
df.corr()
| fixed acidity | volatile acidity | citric acid | residual sugar | chlorides | free sulfur dioxide | total sulfur dioxide | density | pH | sulphates | alcohol | quality | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| fixed acidity | 1.000000 | -0.256131 | 0.671703 | 0.114777 | 0.093705 | -0.153794 | -0.113181 | 0.668047 | -0.682978 | 0.183006 | -0.061668 | 0.124052 |
| volatile acidity | -0.256131 | 1.000000 | -0.552496 | 0.001918 | 0.061298 | -0.010504 | 0.076470 | 0.022026 | 0.234937 | -0.260987 | -0.202288 | -0.390558 |
| citric acid | 0.671703 | -0.552496 | 1.000000 | 0.143577 | 0.203823 | -0.060978 | 0.035533 | 0.364947 | -0.541904 | 0.312770 | 0.109903 | 0.226373 |
| residual sugar | 0.114777 | 0.001918 | 0.143577 | 1.000000 | 0.055610 | 0.187049 | 0.203028 | 0.355283 | -0.085652 | 0.005527 | 0.042075 | 0.013732 |
| chlorides | 0.093705 | 0.061298 | 0.203823 | 0.055610 | 1.000000 | 0.005562 | 0.047400 | 0.200632 | -0.265026 | 0.371260 | -0.221141 | -0.128907 |
| free sulfur dioxide | -0.153794 | -0.010504 | -0.060978 | 0.187049 | 0.005562 | 1.000000 | 0.667666 | -0.021946 | 0.070377 | 0.051658 | -0.069408 | -0.050656 |
| total sulfur dioxide | -0.113181 | 0.076470 | 0.035533 | 0.203028 | 0.047400 | 0.667666 | 1.000000 | 0.071269 | -0.066495 | 0.042947 | -0.205654 | -0.185100 |
| density | 0.668047 | 0.022026 | 0.364947 | 0.355283 | 0.200632 | -0.021946 | 0.071269 | 1.000000 | -0.341699 | 0.148506 | -0.496180 | -0.174919 |
| pH | -0.682978 | 0.234937 | -0.541904 | -0.085652 | -0.265026 | 0.070377 | -0.066495 | -0.341699 | 1.000000 | -0.196648 | 0.205633 | -0.057731 |
| sulphates | 0.183006 | -0.260987 | 0.312770 | 0.005527 | 0.371260 | 0.051658 | 0.042947 | 0.148506 | -0.196648 | 1.000000 | 0.093595 | 0.251397 |
| alcohol | -0.061668 | -0.202288 | 0.109903 | 0.042075 | -0.221141 | -0.069408 | -0.205654 | -0.496180 | 0.205633 | 0.093595 | 1.000000 | 0.476166 |
| quality | 0.124052 | -0.390558 | 0.226373 | 0.013732 | -0.128907 | -0.050656 | -0.185100 | -0.174919 | -0.057731 | 0.251397 | 0.476166 | 1.000000 |
Gdy ktoś pierwszy raz spojrzy na heatmape może się zastanawiać czemu wartości na przekątnej mają wartość jeden i wyraźnie wyróżniają się ciemnym różowym kolorem. Dzieje się tak oczywiście dlatego ponieważ współczynnik korelacji pomiędzy tą samą zmienną musi wynosić 1.
fig = px.imshow(df.corr(),color_continuous_scale="tropic")
fig.show()
fig = px.violin(df,x='quality', y='alcohol',title = "Do Alcohol has a positive impact on the wine quality", labels = {"alcohol" : "Alcohol", "quality" : "Quality"})
fig.show()
możemy zauważyć na wykresie, że rzeczywiście, im wyższa zawartość alkoholu, tym wyższa jakość wina
Dwa poniższe wykresy pomagają zobaczyć ile w dataFrame'a znajduje się win o danym stężeniu alkoholu i danej jakości
fig = px.density_contour(df, x="quality", y="alcohol")
fig.update_traces(contours_coloring="fill", contours_showlabels = True)
fig.show()
fig = px.scatter(df, x="quality", y="alcohol")
fig.update_traces(marker=dict(size=12,
line=dict(width=2,
color='DarkSlateGrey')),
selector=dict(mode='markers'))
fig.show()
fig = px.violin(df,x='quality', y='sulphates',title = "Do Sulphates have a positive impact on the wine quality?", labels = {"sulphates" : "Sulphates", "quality" : "Quality"})
fig.show()
możemy zauważyc, że i tutaj zależność wydaje się być poprawna, im wyższe stężenie siarczanów tym wyższa jakość wina.
Dwa poniższe wykresy pomagają zobaczyć ile w dataFrame'a znajduje się win o danym stężeniu alkoholu i danej jakości
fig = px.density_contour(df, x="quality", y="sulphates")
fig.update_traces(contours_coloring="fill", contours_showlabels = True)
fig.show()
fig = px.scatter(df, x="quality", y="sulphates")
fig.update_traces(marker=dict(size=12,
line=dict(width=2,
color='DarkSlateGrey')),
selector=dict(mode='markers'))
fig.show()
df["quality"] = df["quality"].astype(str)
fig = px.scatter(df, x="alcohol", y="sulphates", color="quality", title = "How do sulphates and alcohol influence the quality of a wine?",
labels = {"alcohol" : "Alcohol", "sulphates" : "Sulphates"})
fig.show()
Stwórzymy jednak oddzielne wykresy dla każdej jakości wina
fig = px.scatter(df, x="alcohol", y="sulphates", facet_col="quality",facet_col_wrap=2)
fig.show()
Większość dobrych win (quality >= 7) posiada ponad 10% alkoholu oraz siarczany pomiędzy 0.5 a 1.0
Co ważne w ramce danych występują głównie wina średniej jakości ( przeważa ocena 5-6, brak natomiast tych z ocena 0-2 oraz 9-10
Zbiór danych zawiera bardzo dużo cennych informacji na temat składu i jakości win. Myślę, że ciekawym byłoby zrobienie modelu predykcyjnego na jego podstawie, który na podstawie składu wina stwierdzałby np. jakiej wino może być jakości.